package tp.tecnicasdisenio.model.mail;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import org.apache.commons.lang3.Validate;

import tp.tecnicasdisenio.model.MailInterface;
import tp.tecnicasdisenio.model.entity.MensajeSMTPMIME;

public class MailParser implements MailInterface {

  private static final String UNKNOWN_VALUE = "";

  private MensajeSMTPMIME mensaje;
  private MimeMessage mimeOriginal;

  public MailParser(final MensajeSMTPMIME unMensaje) {
    Validate.notNull(unMensaje, "El mensaje MIME no puede ser nulo.");
    mensaje = unMensaje;
    InputStream is = new ByteArrayInputStream(mensaje.getMime().getBytes());
    Properties props = new Properties();
    Session session = Session.getDefaultInstance(props, null);
    try {
      mimeOriginal = new MimeMessage(session, is);
    } catch(MessagingException e) {
      e.printStackTrace();
    }
  }

  @Override
  public String getTitulo() {
    try {
      String[] header = mimeOriginal.getHeader("Subject");
      return header[0];
    } catch(MessagingException e) {
      e.printStackTrace(); // Loguear y continuar (debe ser failsafe).
      return UNKNOWN_VALUE;
    }
  }

  @Override
  public String getCuerpoMensaje(final FormatoContenido formato) {
    try {
      Object content = mimeOriginal.getContent();
      if (MimeMultipart.class.isAssignableFrom(content.getClass())) {
        MimeMultipart multipart = (MimeMultipart) content;
        int count = multipart.getCount();
        for (int i = 0; i < count; i++) {
          BodyPart bodyPart = multipart.getBodyPart(i);
          Map<String, Part> foundMimeTypes =
              findMimeTypes(bodyPart, formato.obtenerFormato());
          int size = foundMimeTypes.size();
          if (size > 0) {
            Part atomicPart = foundMimeTypes.get(formato.obtenerFormato());
            if (atomicPart != null) {
              return (String) atomicPart.getContent();
            }
          }
        }
      }
    } catch(IOException e) {
      e.printStackTrace(); // Loguear y continuar.
    } catch(MessagingException e) {
      e.printStackTrace(); // Loguear y continuar.
    }
    return UNKNOWN_VALUE;
  }

  @Override
  public String getMIME() {
    return mensaje.getMime();
  }

  @Override
  public String getFrom() {
    try {
      Address[] addresses = mimeOriginal.getFrom();
      InternetAddress ia = (InternetAddress) addresses[0];
      return ia.getAddress();
    } catch(MessagingException e) {
      e.printStackTrace();
      return UNKNOWN_VALUE; // Loguear y continuar.
    }
  }

  @Override
  public List<String> getTo() {
    List<String> toAddresses = new ArrayList<String>();
    try {
      Address[] recipients = mimeOriginal.getAllRecipients();
      for (Address address : recipients) {
        InternetAddress ia = (InternetAddress) address;
        toAddresses.add(ia.getAddress());
      }
      return toAddresses;
    } catch(MessagingException e) {
      e.printStackTrace();
      return toAddresses; // Loguear y continuar.
    }
  }

  @Override
  public List<File> getAdjuntos() {
    List<File> attachments = new ArrayList<File>();
    try {
      Object content = mimeOriginal.getContent();
      if (Multipart.class.isAssignableFrom(content.getClass())) {

        Multipart multipart = (Multipart) content;
        for (int i = 0; i < multipart.getCount(); i++) {
          BodyPart bodyPart = multipart.getBodyPart(i);
          if (!Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition())) {
            continue;
          }
          InputStream is = bodyPart.getInputStream();
          File f = new File(bodyPart.getFileName());
          FileOutputStream fos = new FileOutputStream(f);
          byte[] buf = new byte[4096];
          int bytesRead;
          while ((bytesRead = is.read(buf)) != -1) {
            fos.write(buf, 0, bytesRead);
          }
          fos.close();
          attachments.add(f);
        }
      }
    } catch(MessagingException e) {
      e.printStackTrace(); // Loguear y continuar.
    } catch(IOException e) {
      e.printStackTrace(); // Loguear y continuar.
    }
    return attachments;
  }

  @Override
  public Date getFecha() {
    try {
      String[] header = mimeOriginal.getHeader("Date");
      SimpleDateFormat sdf =
          new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH);
      Date parse = sdf.parse(header[0]);
      return parse;
    } catch(MessagingException e) {
      e.printStackTrace();
      return null; // Loguear y continuar.
    } catch(ParseException e) {
      e.printStackTrace();
      return null; // Loguear y continuar.
    }
  }

  private Map<String, Part> findMimeTypes(Part p, String... mimeTypes) {
    Map<String, Part> parts = new HashMap<String, Part>();
    findMimeTypesHelper(p, parts, mimeTypes);
    return parts;
  }

  private void findMimeTypesHelper(Part p, Map<String, Part> parts,
      String... mimeTypes) {
    try {
      if (p.isMimeType("multipart/*")) {
        Multipart mp = (Multipart) p.getContent();
        for (int i = 0; i < mp.getCount(); i++) {
          findContentTypesHelper(mp.getBodyPart(i), parts, mimeTypes);
        }
      } else {
        for (String mimeType : mimeTypes) {
          if (p.isMimeType(mimeType) && !parts.containsKey(mimeType)) {
            parts.put(mimeType, p);
          }
        }
      }
    } catch(UnsupportedEncodingException ex) {
      ex.printStackTrace();
    } catch(MessagingException e) {
      e.printStackTrace();
    } catch(IOException e) {
      e.printStackTrace();
    }
  }

  private void findContentTypesHelper(Part p, Map<String, Part> contentTypes,
      String... mimeTypes) throws MessagingException, IOException {
    try {
      if (p.isMimeType("multipart/*")) {
        Multipart mp = (Multipart) p.getContent();
        for (int i = 0; mp != null && i < mp.getCount(); i++) {
          findContentTypesHelper(mp.getBodyPart(i), contentTypes, mimeTypes);
        }
      } else {
        for (String mimeType : mimeTypes) {
          if (p.isMimeType(mimeType)) {
            contentTypes.put(mimeType, p);
          }
        }
      }
    } catch(UnsupportedEncodingException ex) {
      ex.printStackTrace();
    }
  }

	@Override
	public MensajeSMTPMIME getMensaje() {
		return this.mensaje;
	}

	@Override
	public void aplicaRegla() {
		this.mensaje.setAplicoARegla(true);
	}

}
